/*
 *  Copyright (c) 2010 cul.de.sac.code@gmail.com
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

#include <boost/scoped_ptr.hpp>
#include <boost/python.hpp>
#include <datetime.h>
#include <iostream>
#include <string>

namespace bpc = boost::python::converter;
namespace py = boost::python;
using namespace std;

#ifdef WIN32
#define WIN_ENV
#define _FUNC __FUNCSIG__
#else
#define MAC_ENV
#define _FUNC __PRETTY_FUNCTION__
#endif

// Must be defined to instantiate template classes
#define TXMP_STRING_TYPE std::string 

// Must be defined to give access to XMPFiles
#define XMP_INCLUDE_XMPFILES 1 

// Ensure XMP templates are instantiated
#include "XMP.incl_cpp"

// Provide access to the API
#include "XMP.hpp"

#define _TEST_XMP_RESULT(r) do { if(!(r)) throw XMPException(string(_FUNC) + " failed!"); } while(false)

#if !defined(NDEBUG)
#define _TRACE_FUNC() do { cout << _FUNC << endl; } while(false)
#else
#define _TRACE_FUNC() 
#endif

namespace xmp
{
    void translate_exception(exception const& e)
    {
        // Use the Python 'C' API to set up an exception object
        PyErr_SetString(PyExc_Exception, e.what());
    }
    
    void translate_xmp_error(XMP_Error const& e)
    {
        // Use the Python 'C' API to set up an exception object
        stringstream ss;
        ss << e.GetErrMsg() << " (" << e.GetID() << ")";
        PyErr_SetString(PyExc_Exception, ss.str().c_str());
    }
    
    struct xmp2python_datetime
    {
        static PyObject* convert(XMP_DateTime const& xdt)
        {
            return PyDateTime_FromDateAndTime((int)xdt.year,
                                              (int)xdt.month,
                                              (int)xdt.day,
                                              xdt.hour,
                                              xdt.minute,
                                              xdt.second,
                                              xdt.nanoSecond/1000);
        }
    };

    struct python2xmp_datetime
    {
        python2xmp_datetime()
        {
            bpc::registry::push_back(&convertible, &construct, py::type_id<XMP_DateTime>());
        }
        
        static void* convertible(PyObject * pObj)
        {
            return PyDateTime_Check(pObj) ? pObj : NULL;
        }
        
        static void construct(PyObject* pObj, bpc::rvalue_from_python_stage1_data * data)
        {            
            PyDateTime_DateTime const* pPdt = reinterpret_cast<PyDateTime_DateTime*>(pObj);
            
            void* storage = ((bpc::rvalue_from_python_storage<XMP_DateTime>*)data)->storage.bytes;

            XMP_DateTime* pXdt = new (storage) XMP_DateTime();
            pXdt->year         = PyDateTime_GET_YEAR(pPdt);
            pXdt->month        = PyDateTime_GET_MONTH(pPdt);
            pXdt->day          = PyDateTime_GET_DAY(pPdt);
            pXdt->hour         = PyDateTime_DATE_GET_HOUR(pPdt);
            pXdt->minute       = PyDateTime_DATE_GET_MINUTE(pPdt);
            pXdt->second       = PyDateTime_DATE_GET_SECOND(pPdt);
            pXdt->nanoSecond   = PyDateTime_DATE_GET_MICROSECOND(pPdt)*1000;
            
            data->convertible = storage;
        }
    };
    
    void bind_conversions()
    {
        _TRACE_FUNC();

        PyDateTime_IMPORT;
        
        python2xmp_datetime();
                
        py::to_python_converter<const XMP_DateTime, xmp2python_datetime>();
    }

    class XMPException
        : public exception
    {
    public:
        XMPException(const string& what)
            : _what(what)
        {
        }
        
        virtual ~XMPException() throw ()
        {
        }
        
        virtual char const * what() const throw ()
        {
            return _what.c_str();
        }
        
        string _what;
    };
        
    class SXMPMeta
    {
    public:
        SXMPMeta()
            : _spSXMPMeta(new ::SXMPMeta())
        {
            _TRACE_FUNC();
			if(!_spSXMPMeta)
			{
				throw XMPException("No memory");
			}
        }

        SXMPMeta(::SXMPMeta* pMeta)
            : _spSXMPMeta(pMeta)
        {
            _TRACE_FUNC();
			if(!_spSXMPMeta)
			{
				throw XMPException("No memory");
			}
        }
        
        SXMPMeta(TXMP_STRING_TYPE buffer)
            : _spSXMPMeta(new ::SXMPMeta(buffer.c_str(), buffer.size()))
        {
            _TRACE_FUNC();
			if(!_spSXMPMeta)
			{
				throw XMPException("No memory");
			}
        }
        
        static py::object GetVersionInfo()
        {
            _TRACE_FUNC();
            XMP_VersionInfo versionInfo = { 0 };
            ::SXMPMeta::GetVersionInfo(&versionInfo);
            return py::object(versionInfo);
        }
        
        static void Initialize()
        {
            _TRACE_FUNC();
            _TEST_XMP_RESULT(::SXMPMeta::Initialize());
        }
        
        static void Terminate()
        {
            _TRACE_FUNC();
            ::SXMPMeta::Terminate();
        }

        static XMP_OptionBits GetGlobalOptions()
        {
            _TRACE_FUNC();
            return ::SXMPMeta::GetGlobalOptions();
        }
        
        static void SetGlobalOptions(XMP_OptionBits options)
        {
            _TRACE_FUNC();
            ::SXMPMeta::SetGlobalOptions(options);
        }
        
        static XMP_Status DumpNamespaces(py::object& outProc, py::object& clientData)
        {
            _TRACE_FUNC();
            DumpParameters dumpParameters = { &outProc, &clientData };
            return ::SXMPMeta::DumpNamespaces(XMP_TextOutputCallback, &dumpParameters);
        }
        
        static py::object RegisterNamespace(XMP_StringPtr namespaceURI, XMP_StringPtr suggestedPrefix)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE registeredPrefix;
            ::SXMPMeta::RegisterNamespace(namespaceURI, suggestedPrefix, &registeredPrefix);
            return py::object(registeredPrefix);
        }
        
        static py::object GetNamespacePrefix(XMP_StringPtr namespaceURI)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE namespacePrefix;
            ::SXMPMeta::GetNamespacePrefix(namespaceURI, &namespacePrefix);
            return py::object(namespacePrefix);
        }
        
        static py::object GetNamespaceURI(XMP_StringPtr namespacePrefix)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE namespaceURI;
            ::SXMPMeta::GetNamespaceURI(namespacePrefix, &namespaceURI);
            return py::object(namespaceURI);
        }
        
        static void DeleteNamespace(XMP_StringPtr namespaceURI)
        {
            _TRACE_FUNC();
            ::SXMPMeta::DeleteNamespace(namespaceURI);
        }
                
        py::tuple GetProperty(XMP_StringPtr schemaNS, XMP_StringPtr propName)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE propValue;
            XMP_OptionBits options;
            return _spSXMPMeta->GetProperty(schemaNS, propName, &propValue, &options)
                ? py::make_tuple(propValue, options)
                : py::make_tuple();
        }

        py::tuple GetArrayItem(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_Index itemIndex) const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE itemValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetArrayItem(schemaNS, arrayName, itemIndex, &itemValue, &options)
                ? py::make_tuple(itemValue, options)
                : py::make_tuple();
        }
        
        py::tuple GetStructField(XMP_StringPtr schemaNS, XMP_StringPtr structName, XMP_StringPtr fieldNS, XMP_StringPtr fieldName) const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE fieldValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetStructField(schemaNS, structName, fieldNS, fieldName, &fieldValue, &options)
                ? py::make_tuple(fieldValue, options)
                : py::make_tuple();
        }
        
        py::tuple GetQualifier(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_StringPtr qualNS, XMP_StringPtr qualName) const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE qualValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetQualifier(schemaNS, propName, qualNS, qualName, &qualValue, &options)
                ? py::make_tuple(qualValue, options)
                : py::make_tuple();
        }
        
        void SetProperty(XMP_StringPtr schemaNS, XMP_StringPtr propName, const TXMP_STRING_TYPE & propValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetProperty(schemaNS, propName, propValue, options);
        }
        
        void SetArrayItem(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_Index itemIndex, const TXMP_STRING_TYPE & itemValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetArrayItem(schemaNS, arrayName, itemIndex, itemValue, options);
        }
        
        void AppendArrayItem(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_OptionBits arrayOptions, const TXMP_STRING_TYPE & itemValue, XMP_OptionBits itemOptions = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->AppendArrayItem(schemaNS, arrayName, arrayOptions, itemValue, itemOptions);
        }
        
        void SetStructField(XMP_StringPtr schemaNS, XMP_StringPtr structName, XMP_StringPtr fieldNS, XMP_StringPtr fieldName, const TXMP_STRING_TYPE & fieldValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetStructField(schemaNS, structName, fieldNS, fieldName, fieldValue, options);
        }
        
        void SetQualifier(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_StringPtr qualNS, XMP_StringPtr qualName, const TXMP_STRING_TYPE & qualValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetQualifier(schemaNS, propName, qualNS, qualName, qualValue, options);
        }
        
        void DeleteProperty(XMP_StringPtr schemaNS, XMP_StringPtr propName)
        {
            _TRACE_FUNC();
            _spSXMPMeta->DeleteProperty(schemaNS, propName);
        }
        
        void DeleteArrayItem(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_Index itemIndex)
        {
            _TRACE_FUNC();
            _spSXMPMeta->DeleteArrayItem(schemaNS, arrayName, itemIndex);
        }
        
        void DeleteStructField(XMP_StringPtr schemaNS, XMP_StringPtr structName, XMP_StringPtr fieldNS, XMP_StringPtr fieldName)
        {
            _TRACE_FUNC();
            _spSXMPMeta->DeleteStructField(schemaNS, structName, fieldNS, fieldName);
        }
        
        void DeleteQualifier(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_StringPtr qualNS, XMP_StringPtr qualName)
        {
            _TRACE_FUNC();
            _spSXMPMeta->DeleteQualifier(schemaNS, propName, qualNS, qualName);
        }
        
        bool DoesPropertyExist(XMP_StringPtr schemaNS, XMP_StringPtr propName ) const
        {
            _TRACE_FUNC();
            return _spSXMPMeta->DoesPropertyExist(schemaNS, propName);
        }
        
        bool DoesArrayItemExist(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_Index itemIndex ) const
        {
            _TRACE_FUNC();
            return _spSXMPMeta->DoesArrayItemExist(schemaNS, arrayName, itemIndex);
        }
        
        bool DoesStructFieldExist(XMP_StringPtr schemaNS, XMP_StringPtr structName, XMP_StringPtr fieldNS, XMP_StringPtr fieldName ) const
        {
            _TRACE_FUNC();
            return _spSXMPMeta->DoesStructFieldExist(schemaNS, structName, fieldNS, fieldName);
        }
        
        bool DoesQualifierExist(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_StringPtr qualNS, XMP_StringPtr qualName ) const
        {
            _TRACE_FUNC();
           return  _spSXMPMeta->DoesQualifierExist(schemaNS, propName, qualNS, qualName);
        }
        
        py::tuple GetProperty_Bool(XMP_StringPtr schemaNS, XMP_StringPtr propName) const
        {
            _TRACE_FUNC();
            bool propValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetProperty_Bool(schemaNS, propName, &propValue, &options)
                ? py::make_tuple(propValue, options)
                : py::make_tuple();
        }
        
        py::tuple GetProperty_Int(XMP_StringPtr schemaNS, XMP_StringPtr propName) const
        {
            _TRACE_FUNC();
            XMP_Int32 propValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetProperty_Int(schemaNS, propName, &propValue, &options)
                ? py::make_tuple(propValue, options)
                : py::make_tuple();
        }
        
        py::tuple GetProperty_Int64(XMP_StringPtr schemaNS, XMP_StringPtr propName) const
        {
            _TRACE_FUNC();
            XMP_Int64 propValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetProperty_Int64(schemaNS, propName, &propValue, &options)
                ? py::make_tuple(propValue, options)
                : py::make_tuple();
        }
        
        py::tuple GetProperty_Float(XMP_StringPtr schemaNS, XMP_StringPtr propName) const
        {
            _TRACE_FUNC();
            double propValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetProperty_Float(schemaNS, propName, &propValue, &options)
                ? py::make_tuple(propValue, options)
                : py::make_tuple();
        }
        
        py::tuple GetProperty_Date(XMP_StringPtr schemaNS, XMP_StringPtr propName) const
        {
            _TRACE_FUNC();
            XMP_DateTime propValue;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetProperty_Date(schemaNS, propName, &propValue, &options)
                ? py::make_tuple(propValue, options)
                : py::make_tuple();
        }
        
        void SetProperty_Bool(XMP_StringPtr schemaNS, XMP_StringPtr propName, bool propValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetProperty_Bool( schemaNS, propName, propValue, options);
        }
        
        void SetProperty_Int(XMP_StringPtr schemaNS, XMP_StringPtr propName, long propValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetProperty_Int( schemaNS, propName, propValue, options);
        }
        
        void SetProperty_Int64(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_Int64 propValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetProperty_Int64( schemaNS, propName, propValue, options);
        }
        
        void SetProperty_Float(XMP_StringPtr schemaNS, XMP_StringPtr propName, double propValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetProperty_Float( schemaNS, propName, propValue, options);
        }
        
        void SetProperty_Date(XMP_StringPtr schemaNS, XMP_StringPtr propName, /*const XMP_DateTime &*/py::object  propValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            XMP_DateTime dt = py::extract<XMP_DateTime>(propValue);
            _spSXMPMeta->SetProperty_Date(schemaNS, propName, dt, options);
        }
        
        py::tuple GetLocalizedText(XMP_StringPtr schemaNS, XMP_StringPtr altTextName, XMP_StringPtr genericLang, XMP_StringPtr specificLang) const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE itemValue;
            TXMP_STRING_TYPE actualLang;
            XMP_OptionBits options = 0;
            return _spSXMPMeta->GetLocalizedText(schemaNS, altTextName, genericLang, specificLang, &actualLang, &itemValue, &options)
                ? py::make_tuple(itemValue, actualLang, options)
                : py::make_tuple();
        }
        
        void SetLocalizedText(XMP_StringPtr schemaNS, XMP_StringPtr altTextName, XMP_StringPtr genericLang, XMP_StringPtr specificLang, const TXMP_STRING_TYPE & itemValue, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetLocalizedText(schemaNS, altTextName, genericLang, specificLang, itemValue, options);
        }

        void DeleteLocalizedText(XMP_StringPtr schemaNS, XMP_StringPtr altTextName, XMP_StringPtr genericLang, XMP_StringPtr specificLang)
        {
            _TRACE_FUNC();
            _spSXMPMeta->DeleteLocalizedText(schemaNS, altTextName, genericLang, specificLang);
        }
        
        void ParseFromBuffer(TXMP_STRING_TYPE buffer, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            _spSXMPMeta->ParseFromBuffer(buffer.c_str(), buffer.size(), options);
        }
        
        TXMP_STRING_TYPE SerializeToBuffer(XMP_OptionBits options, XMP_StringLen  padding, XMP_StringPtr newline, XMP_StringPtr indent = "", XMP_Index baseIndent = 0 ) const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE rdfString;
            _spSXMPMeta->SerializeToBuffer(&rdfString, options, padding, newline, indent, baseIndent);
            return rdfString;
        }
        
        TXMP_STRING_TYPE SerializeToBuffer(XMP_OptionBits options = 0, XMP_StringLen  padding = 0 ) const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE rdfString;
            _spSXMPMeta->SerializeToBuffer(&rdfString, options, padding);
            return rdfString;
        }
       
        TXMP_STRING_TYPE GetObjectName() const
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE name;
            _spSXMPMeta->GetObjectName(&name);
            return name;
        }
        
        void SetObjectName(TXMP_STRING_TYPE name)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetObjectName(name);
        }
        
        void Sort()
        {
            _TRACE_FUNC();
            _spSXMPMeta->Sort();
        }
        
        void Erase()
        {
            _TRACE_FUNC();
            _spSXMPMeta->Erase();
        }
        
        xmp::SXMPMeta Clone(XMP_OptionBits options = 0 ) const
        {
            _TRACE_FUNC();
            ::SXMPMeta * clone = new ::SXMPMeta(_spSXMPMeta->Clone(options));
            xmp::SXMPMeta meta(clone);
            return meta;
        }
        
        XMP_Index CountArrayItems(XMP_StringPtr schemaNS, XMP_StringPtr arrayName ) const
        {
            _TRACE_FUNC();
            return _spSXMPMeta->CountArrayItems(schemaNS, arrayName);
        }

        XMP_Status DumpObject(py::object& outProc, py::object& clientData) const
        {
            _TRACE_FUNC();
            DumpParameters dumpParameters = { &outProc, &clientData };
            return _spSXMPMeta->DumpObject(XMP_TextOutputCallback, &dumpParameters);
        }
        
        XMP_OptionBits GetObjectOptions() const
        {
            _TRACE_FUNC();
            return _spSXMPMeta->GetObjectOptions();
        }
        
        void SetObjectOptions(XMP_OptionBits options)
        {
            _TRACE_FUNC();
            _spSXMPMeta->SetObjectOptions(options);
        }
                
        ::SXMPMeta* getSXMPMeta()
        {
            return _spSXMPMeta.get();
        }
        
        ::SXMPMeta const& getSXMPMeta() const
        {
            return *_spSXMPMeta.get();
        }
        
    private:
        struct DumpParameters
        {
            py::object*  pyProc;
            py::object*  clientData;
        };
        
        static XMP_Status XMP_TextOutputCallback(void * refCon, XMP_StringPtr buffer, XMP_StringLen bufferSize)
        {
            _TRACE_FUNC();
            DumpParameters* pDumpParameters = static_cast<DumpParameters*>(refCon);
            return boost::python::call<XMP_Status>(pDumpParameters->pyProc->ptr(), *pDumpParameters->clientData, buffer, bufferSize);
        }
        
        boost::shared_ptr< ::SXMPMeta> _spSXMPMeta;
    };

    class SXMPFiles
    {
    public:
        SXMPFiles()
            : _spSXMPFiles(new ::SXMPFiles())
        {
            _TRACE_FUNC();
			if(!_spSXMPFiles)
			{
				throw XMPException("No memory");
			}
        }

        SXMPFiles(XMP_StringPtr filePath, XMP_FileFormat format = kXMP_UnknownFile, XMP_OptionBits openFlags = 0)
            : _spSXMPFiles(new ::SXMPFiles(filePath, format, openFlags))
        {
            _TRACE_FUNC();
			if(!_spSXMPFiles)
			{
				throw XMPException("No memory");
			}
        }
        
        static py::object GetVersionInfo()
        {
            _TRACE_FUNC();
            XMP_VersionInfo versionInfo = { 0 };
            ::SXMPFiles::GetVersionInfo(&versionInfo);
            return py::object(versionInfo);
        }

        static void Initialize()
        {
            _TRACE_FUNC();
            _TEST_XMP_RESULT(::SXMPFiles::Initialize());
        }
        
        static void Initialize(XMP_OptionBits options)
        {
            _TRACE_FUNC();
            _TEST_XMP_RESULT(::SXMPFiles::Initialize(options));
        }

        static void Terminate()
        {
            _TRACE_FUNC();
            ::SXMPFiles::Terminate();
        }

        static py::tuple GetFormatInfo(XMP_FileFormat format)
        {
            _TRACE_FUNC();
            XMP_OptionBits handlerFlags;
            bool smart = ::SXMPFiles::GetFormatInfo(format, &handlerFlags);
            return py::make_tuple(smart, handlerFlags);
        }

        static XMP_FileFormat CheckFileFormat(XMP_StringPtr filePath)
        {
            _TRACE_FUNC();
            return ::SXMPFiles::CheckFileFormat(filePath);
        }

        static XMP_FileFormat CheckPackageFormat(XMP_StringPtr folderPath)
        {
            _TRACE_FUNC();
            return ::SXMPFiles::CheckPackageFormat(folderPath);
        }

        void OpenFile(const TXMP_STRING_TYPE& filePath, XMP_FileFormat format = kXMP_UnknownFile, XMP_OptionBits openFlags = 0)
        {
            _TRACE_FUNC();
            _TEST_XMP_RESULT(_spSXMPFiles->OpenFile(filePath, format, openFlags));
        }
        
        void CloseFile(XMP_OptionBits closeFlags = 0)
        {
            _TRACE_FUNC();
            _spSXMPFiles->CloseFile(closeFlags);
        }
        
        py::tuple GetFileInfo()
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE filePath;
            XMP_OptionBits openFlags;
            XMP_FileFormat format;
            XMP_OptionBits handlerFlags;
            _TEST_XMP_RESULT(_spSXMPFiles->GetFileInfo(&filePath, &openFlags, &format, &handlerFlags));
            return py::make_tuple(filePath, openFlags, format, handlerFlags);
        }
        
        void SetAbortProc(py::object& abortProc, py::object& abortArg)
        {
            _TRACE_FUNC();
            _abortParameters.pyProc = abortProc;
            _abortParameters.arg = abortArg;
            _spSXMPFiles->SetAbortProc(XMP_AbortProc, &_abortParameters);
        }

        py::tuple GetXMP()
        {
            _TRACE_FUNC();
            xmp::SXMPMeta xmpObj;
            TXMP_STRING_TYPE xmpPacket;
            XMP_PacketInfo packetInfo;
            return _spSXMPFiles->GetXMP(xmpObj.getSXMPMeta(), &xmpPacket, &packetInfo)
                ? py::make_tuple(xmpObj, xmpPacket, packetInfo)
                : py::make_tuple();
        }

        void PutXMP(const xmp::SXMPMeta& xmpObj)
        {
            _TRACE_FUNC();
            _spSXMPFiles->PutXMP(xmpObj.getSXMPMeta());
        }
        
        void PutXMP(const TXMP_STRING_TYPE& xmpPacket)
        {
            _TRACE_FUNC();
            _spSXMPFiles->PutXMP(xmpPacket);
        }
        
        bool CanPutXMP(const xmp::SXMPMeta& xmpObj)
        {
            _TRACE_FUNC();
            return _spSXMPFiles->CanPutXMP(xmpObj.getSXMPMeta());
        }
        
        bool CanPutXMP(const TXMP_STRING_TYPE& xmpPacket)
        {
            _TRACE_FUNC();
            return _spSXMPFiles->CanPutXMP(xmpPacket);
        }
        
    private:
        struct AbortParameters
        {
            py::object pyProc;
            py::object arg;
        };
        
        static bool XMP_AbortProc(void * arg)
        {
            _TRACE_FUNC();
            AbortParameters* pAbortParameters = static_cast<AbortParameters*>(arg);
            return boost::python::call<bool>(pAbortParameters->pyProc.ptr(), pAbortParameters->arg);
        }

        AbortParameters _abortParameters;
        boost::shared_ptr< ::SXMPFiles> _spSXMPFiles;
    };
    
    class SXMPIterator
    {
    public:
        SXMPIterator(const xmp::SXMPMeta& xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options = 0)
            : _spSXMPIterator(new ::SXMPIterator(xmpObj.getSXMPMeta(), schemaNS, propName, options))
        {
            _TRACE_FUNC();
			if(!_spSXMPIterator)
			{
				throw XMPException("No memory");
			}
        }
        
        SXMPIterator(const xmp::SXMPMeta& xmpObj, XMP_StringPtr schemaNS, XMP_OptionBits options = 0)
            : _spSXMPIterator(new ::SXMPIterator(xmpObj.getSXMPMeta(), schemaNS, options))
        {
            _TRACE_FUNC();
			if(!_spSXMPIterator)
			{
				throw XMPException("No memory");
			}
        }
        
        SXMPIterator(const xmp::SXMPMeta& xmpObj, XMP_OptionBits options = 0)
            : _spSXMPIterator(new ::SXMPIterator(xmpObj.getSXMPMeta(), options))
        {
            _TRACE_FUNC();
			if(!_spSXMPIterator)
			{
				throw XMPException("No memory");
			}
        }
        
        SXMPIterator(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_OptionBits options)
            : _spSXMPIterator(new ::SXMPIterator(schemaNS, propName, options))
        {
            _TRACE_FUNC();
			if(!_spSXMPIterator)
			{
				throw XMPException("No memory");
			}
        }

        py::tuple Next()
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE schemaNS;
            TXMP_STRING_TYPE propPath;
            TXMP_STRING_TYPE propValue;
            XMP_OptionBits options;
            if(_spSXMPIterator->Next(&schemaNS, &propPath, &propValue, &options))
            {
                return py::make_tuple(schemaNS, propPath, propValue, options);
            }
            else
            {
                return py::make_tuple();
            }
        }
        
        void Skip(XMP_OptionBits options)
        {
            _TRACE_FUNC();
            _spSXMPIterator->Skip(options);
        }
                              
    private:
        boost::shared_ptr< ::SXMPIterator> _spSXMPIterator;                              
    };

    class SXMPUtils
    {
    public:
        static TXMP_STRING_TYPE ComposeArrayItemPath(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_Index itemIndex)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE fullPath;
            ::SXMPUtils::ComposeArrayItemPath(schemaNS, arrayName, itemIndex, &fullPath);
            return fullPath;
        }
        
        static TXMP_STRING_TYPE ComposeStructFieldPath(XMP_StringPtr schemaNS, XMP_StringPtr structName, XMP_StringPtr fieldNS, XMP_StringPtr fieldName)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE fullPath;
            ::SXMPUtils::ComposeStructFieldPath(schemaNS, structName, fieldNS, fieldName, &fullPath);
            return fullPath;
        }
        
        static TXMP_STRING_TYPE ComposeQualifierPath(XMP_StringPtr schemaNS, XMP_StringPtr propName, XMP_StringPtr qualNS, XMP_StringPtr qualName)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE fullPath;
            ::SXMPUtils::ComposeQualifierPath(schemaNS, propName, qualNS, qualName, &fullPath);
            return fullPath;
        }

        static TXMP_STRING_TYPE ComposeLangSelector(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, const TXMP_STRING_TYPE & langName)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE fullPath;
            ::SXMPUtils::ComposeLangSelector(schemaNS, arrayName, langName, &fullPath);
            return fullPath;
        }
        
        static TXMP_STRING_TYPE ComposeFieldSelector(XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_StringPtr fieldNS, XMP_StringPtr fieldName, const TXMP_STRING_TYPE & fieldValue)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE fullPath;
            ::SXMPUtils::ComposeFieldSelector(schemaNS, arrayName, fieldNS, fieldName, fieldValue, &fullPath);
            return fullPath;
        }
        
        static TXMP_STRING_TYPE ConvertFromBool(bool binValue)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE strValue;
            ::SXMPUtils::ConvertFromBool( binValue, &strValue);
            return strValue;
        }
        
        static TXMP_STRING_TYPE ConvertFromInt(long  binValue, XMP_StringPtr format)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE strValue;
            ::SXMPUtils::ConvertFromInt(binValue, format, &strValue);
            return strValue;
        }
        
        static TXMP_STRING_TYPE ConvertFromInt64(XMP_Int64  binValue, XMP_StringPtr format)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE strValue;
            ::SXMPUtils::ConvertFromInt64(binValue, format, &strValue);
            return strValue;
        }
        
        static TXMP_STRING_TYPE ConvertFromFloat(double  binValue, XMP_StringPtr format)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE strValue;
            ::SXMPUtils::ConvertFromFloat(binValue, format, &strValue);
            return strValue;
        }
        
        static TXMP_STRING_TYPE ConvertFromDate(/*const XMP_DateTime &*/py::object binValue)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE strValue;
            XMP_DateTime dt = py::extract<XMP_DateTime>(binValue);
            ::SXMPUtils::ConvertFromDate(dt, &strValue);
            return strValue;
        }
        
        static bool ConvertToBool(const TXMP_STRING_TYPE & strValue)
        {
            _TRACE_FUNC();
            return ::SXMPUtils::ConvertToBool(strValue);
        }
        
        static long ConvertToInt(const TXMP_STRING_TYPE & strValue)
        {
            _TRACE_FUNC();
            return ::SXMPUtils::ConvertToInt(strValue);
        }
        
        static XMP_Int64 ConvertToInt64(const TXMP_STRING_TYPE & strValue)
        {
            _TRACE_FUNC();
            return ::SXMPUtils::ConvertToInt(strValue);
        }
        
        static double ConvertToFloat(const TXMP_STRING_TYPE & strValue)
        {
            _TRACE_FUNC();
            return ::SXMPUtils::ConvertToFloat(strValue);
        }
        
        static py::object ConvertToDate(const TXMP_STRING_TYPE & strValue)
        {
            _TRACE_FUNC();
            XMP_DateTime binValue;
            ::SXMPUtils::ConvertToDate(strValue, &binValue);
            return py::object(binValue);
        }
        
        static py::object CurrentDateTime()
        {
            _TRACE_FUNC();
            XMP_DateTime time;
            ::SXMPUtils::CurrentDateTime(&time);
            return py::object(time);
        }
        
        static py::object SetTimeZone(/*XMP_DateTime*/py::object time)
        {
            _TRACE_FUNC();
            XMP_DateTime dt = py::extract<XMP_DateTime>(time);
            ::SXMPUtils::SetTimeZone(&dt);
            return py::object(dt);
        }
        
        static py::object ConvertToUTCTime(/*XMP_DateTime*/py::object time)
        {
            _TRACE_FUNC();
            XMP_DateTime dt = py::extract<XMP_DateTime>(time);
            ::SXMPUtils::ConvertToUTCTime(&dt);
            return py::object(dt);
        }
        
        static py::object ConvertToLocalTime(/*XMP_DateTime*/py::object time)
        {
            _TRACE_FUNC();
            XMP_DateTime dt = py::extract<XMP_DateTime>(time);
            ::SXMPUtils::ConvertToLocalTime(&dt);
            return py::object(dt);
        }
        
        static int CompareDateTime(py::object left, py::object right)
        {
            _TRACE_FUNC();
            XMP_DateTime lhs = py::extract<XMP_DateTime>(left);
            XMP_DateTime rhs = py::extract<XMP_DateTime>(right);
            return ::SXMPUtils::CompareDateTime(lhs, rhs);
        }
        
        static TXMP_STRING_TYPE EncodeToBase64(const TXMP_STRING_TYPE & rawStr)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE encodedStr;
            ::SXMPUtils::EncodeToBase64(rawStr, &encodedStr);
            return encodedStr;
        }
        
        static TXMP_STRING_TYPE DecodeFromBase64(const TXMP_STRING_TYPE & encodedStr)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE rawStr;
            ::SXMPUtils::DecodeFromBase64(encodedStr, &rawStr);
            return rawStr;
        }
        
        static py::tuple PackageForJPEG(const SXMPMeta & xmpObj)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE standardXMP;
            TXMP_STRING_TYPE extendedXMP;
            TXMP_STRING_TYPE extendedDigest;
            ::SXMPUtils::PackageForJPEG(xmpObj.getSXMPMeta(), &standardXMP, &extendedXMP, &extendedDigest);
            return py::make_tuple(standardXMP, extendedXMP, extendedDigest);
        }
        
        static void MergeFromJPEG(SXMPMeta * fullXMP, const SXMPMeta & extendedXMP)
        {
            _TRACE_FUNC();
            ::SXMPUtils::MergeFromJPEG(fullXMP->getSXMPMeta(), extendedXMP.getSXMPMeta());
        }
        
        static TXMP_STRING_TYPE CatenateArrayItems(const SXMPMeta& xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_StringPtr separator, XMP_StringPtr quotes, XMP_OptionBits options)
        {
            _TRACE_FUNC();
            TXMP_STRING_TYPE catedStr;
            ::SXMPUtils::CatenateArrayItems(xmpObj.getSXMPMeta(), schemaNS, arrayName, separator, quotes, options, &catedStr);
            return catedStr;
        }
        
        static void SeparateArrayItems(SXMPMeta * xmpObj, XMP_StringPtr schemaNS, XMP_StringPtr arrayName, XMP_OptionBits options, const TXMP_STRING_TYPE & catedStr)
        {
            _TRACE_FUNC();
            ::SXMPUtils::SeparateArrayItems(xmpObj->getSXMPMeta(), schemaNS, arrayName, options, catedStr);
        }
        
        static void ApplyTemplate (SXMPMeta * workingXMP, const SXMPMeta & templateXMP, XMP_OptionBits actions)
        {
            _TRACE_FUNC();
            ::SXMPUtils::ApplyTemplate(workingXMP->getSXMPMeta(), templateXMP.getSXMPMeta(), actions);
        }
        
        static void RemoveProperties(SXMPMeta * xmpObj, XMP_StringPtr schemaNS = 0, XMP_StringPtr propName = 0, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            ::SXMPUtils::RemoveProperties(xmpObj->getSXMPMeta(), schemaNS, propName, options);
        }
                
        static void DuplicateSubtree(const SXMPMeta & source, SXMPMeta * dest, XMP_StringPtr sourceNS, XMP_StringPtr sourceRoot, XMP_StringPtr destNS = 0, XMP_StringPtr destRoot = 0, XMP_OptionBits options = 0)
        {
            _TRACE_FUNC();
            ::SXMPUtils::DuplicateSubtree(source.getSXMPMeta(), dest->getSXMPMeta(), sourceNS, sourceRoot, destNS, destRoot, options);
        }
    };    
}

// SXMPFiles
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(OpenFile_overloads, OpenFile, 1, 3)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(CloseFile_overloads, CloseFile, 0, 1)

// SXMPMeta
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetProperty_overloads, SetProperty, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetArrayItem_overloads, SetArrayItem, 4, 5)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(AppendArrayItem_overloads, AppendArrayItem, 4, 5)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetStructField_overloads, SetStructField, 5, 6)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetQualifier_overloads, SetQualifier, 5, 6)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetProperty_Bool_overloads, SetProperty_Bool, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetProperty_Int_overloads, SetProperty_Int, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetProperty_Int64_overloads, SetProperty_Int64, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetProperty_Float_overloads, SetProperty_Float, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetProperty_Date_overloads, SetProperty_Date, 3, 4)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SetLocalizedText_overloads, SetLocalizedText, 5, 6)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ParseFromBuffer_overloads, ParseFromBuffer, 1, 2)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SerializeToBuffer1_overloads, SerializeToBuffer, 3, 5)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(SerializeToBuffer2_overloads, SerializeToBuffer, 0, 2)
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(Clone_overloads, Clone, 0, 1)

// SXMPUtils
BOOST_PYTHON_FUNCTION_OVERLOADS(RemoveProperties_overloads, xmp::SXMPUtils::RemoveProperties, 1, 4)
BOOST_PYTHON_FUNCTION_OVERLOADS(DuplicateSubtree_overloads, xmp::SXMPUtils::DuplicateSubtree, 4, 7)

// Python requires an exported function called init<module-name> in every
// extension module. This is where we build the module contents.
BOOST_PYTHON_MODULE(xmp)
{
    py::register_exception_translator<exception>(&xmp::translate_exception);
    py::register_exception_translator<XMP_Error>(&xmp::translate_xmp_error);
 
    xmp::bind_conversions();
    
    py::docstring_options doc_options(true);
        
    py::scope().attr("kXMP_TrueStr") = kXMP_TrueStr;
    py::scope().attr("kXMP_FalseStr") = kXMP_FalseStr;
    py::scope().attr("kXMPTS_Yes") = static_cast<XMP_Uns32>(kXMPTS_Yes);
    py::scope().attr("kXMPTS_No") = static_cast<XMP_Uns32>(kXMPTS_No);
    py::scope().attr("kXMPTS_Maybe") = static_cast<XMP_Uns32>(kXMPTS_Maybe);
    py::scope().attr("kXMP_TimeWestOfUTC") = static_cast<XMP_Uns32>(kXMP_TimeWestOfUTC);
    py::scope().attr("kXMP_TimeIsUTC") = static_cast<XMP_Uns32>(kXMP_TimeIsUTC);
    py::scope().attr("kXMP_TimeEastOfUTC") = static_cast<XMP_Uns32>(kXMP_TimeEastOfUTC);
    py::scope().attr("kXMP_NS_XMP") = kXMP_NS_XMP;
    py::scope().attr("kXMP_NS_XMP_Rights") = kXMP_NS_XMP_Rights;
    py::scope().attr("kXMP_NS_XMP_MM") = kXMP_NS_XMP_MM;
    py::scope().attr("kXMP_NS_XMP_BJ") = kXMP_NS_XMP_BJ;
    py::scope().attr("kXMP_NS_PDF") = kXMP_NS_PDF;
    py::scope().attr("kXMP_NS_Photoshop") = kXMP_NS_Photoshop;
    py::scope().attr("kXMP_NS_PSAlbum") = kXMP_NS_PSAlbum;
    py::scope().attr("kXMP_NS_EXIF") = kXMP_NS_EXIF;
    py::scope().attr("kXMP_NS_EXIF_Aux") = kXMP_NS_EXIF_Aux;
    py::scope().attr("kXMP_NS_TIFF") = kXMP_NS_TIFF;
    py::scope().attr("kXMP_NS_PNG") = kXMP_NS_PNG;
    py::scope().attr("kXMP_NS_SWF") = kXMP_NS_SWF;
    py::scope().attr("kXMP_NS_JPEG") = kXMP_NS_JPEG;
    py::scope().attr("kXMP_NS_JP2K") = kXMP_NS_JP2K;
    py::scope().attr("kXMP_NS_CameraRaw") = kXMP_NS_CameraRaw;
    py::scope().attr("kXMP_NS_DM") = kXMP_NS_DM;
    py::scope().attr("kXMP_NS_Script") = kXMP_NS_Script;
    py::scope().attr("kXMP_NS_ASF") = kXMP_NS_ASF;
    py::scope().attr("kXMP_NS_WAV") = kXMP_NS_WAV;
    py::scope().attr("kXMP_NS_BWF") = kXMP_NS_BWF;
    py::scope().attr("kXMP_NS_XMP_Note") = kXMP_NS_XMP_Note;
    py::scope().attr("kXMP_NS_AdobeStockPhoto") = kXMP_NS_AdobeStockPhoto;
    py::scope().attr("kXMP_NS_CreatorAtom") = kXMP_NS_CreatorAtom;
    py::scope().attr("kXMP_NS_XMP_IdentifierQual") = kXMP_NS_XMP_IdentifierQual;
    py::scope().attr("kXMP_NS_XMP_Dimensions") = kXMP_NS_XMP_Dimensions;
    py::scope().attr("kXMP_NS_XMP_Text") = kXMP_NS_XMP_Text;
    py::scope().attr("kXMP_NS_XMP_PagedFile") = kXMP_NS_XMP_PagedFile;
    py::scope().attr("kXMP_NS_XMP_Graphics") = kXMP_NS_XMP_Graphics;
    py::scope().attr("kXMP_NS_XMP_Image") = kXMP_NS_XMP_Image;
    py::scope().attr("kXMP_NS_XMP_Font") = kXMP_NS_XMP_Font;
    py::scope().attr("kXMP_NS_XMP_ResourceEvent") = kXMP_NS_XMP_ResourceEvent;
    py::scope().attr("kXMP_NS_XMP_ResourceRef") = kXMP_NS_XMP_ResourceRef;
    py::scope().attr("kXMP_NS_XMP_ST_Version") = kXMP_NS_XMP_ST_Version;
    py::scope().attr("kXMP_NS_XMP_ST_Job") = kXMP_NS_XMP_ST_Job;
    py::scope().attr("kXMP_NS_XMP_ManifestItem") = kXMP_NS_XMP_ManifestItem;
    py::scope().attr("kXMP_NS_XMP_T") = kXMP_NS_XMP_T;
    py::scope().attr("kXMP_NS_XMP_T_PG") = kXMP_NS_XMP_T_PG;
    py::scope().attr("kXMP_NS_XMP_G_IMG") = kXMP_NS_XMP_G_IMG;
    py::scope().attr("kXMP_NS_DC") = kXMP_NS_DC;
    py::scope().attr("kXMP_NS_IPTCCore") = kXMP_NS_IPTCCore;
    py::scope().attr("kXMP_NS_DICOM") = kXMP_NS_DICOM;
    py::scope().attr("kXMP_NS_PDFA_Schema") = kXMP_NS_PDFA_Schema;
    py::scope().attr("kXMP_NS_PDFA_Property") = kXMP_NS_PDFA_Property;
    py::scope().attr("kXMP_NS_PDFA_Type") = kXMP_NS_PDFA_Type;
    py::scope().attr("kXMP_NS_PDFA_Field") = kXMP_NS_PDFA_Field;
    py::scope().attr("kXMP_NS_PDFA_ID") = kXMP_NS_PDFA_ID;
    py::scope().attr("kXMP_NS_PDFA_Extension") = kXMP_NS_PDFA_Extension;
    py::scope().attr("kXMP_NS_PDFX") = kXMP_NS_PDFX;
    py::scope().attr("kXMP_NS_PDFX_ID") = kXMP_NS_PDFX_ID;
    py::scope().attr("kXMP_NS_RDF") = kXMP_NS_RDF;
    py::scope().attr("kXMP_NS_XML") = kXMP_NS_XML;
    py::scope().attr("kXMP_ArrayLastItem") = kXMP_ArrayLastItem;
    py::scope().attr("kXMP_UseNullTermination") = kXMP_UseNullTermination;
    py::scope().attr("kXMP_NoOptions") = kXMP_NoOptions;
    py::scope().attr("kXMP_PropValueIsURI") = static_cast<XMP_Uns32>(kXMP_PropValueIsURI);    
    py::scope().attr("kXMP_PropHasQualifiers") = static_cast<XMP_Uns32>(kXMP_PropHasQualifiers);
    py::scope().attr("kXMP_PropIsQualifier") = static_cast<XMP_Uns32>(kXMP_PropIsQualifier);
    py::scope().attr("kXMP_PropHasLang") = static_cast<XMP_Uns32>(kXMP_PropHasLang);
    py::scope().attr("kXMP_PropHasType") = static_cast<XMP_Uns32>(kXMP_PropHasType);
    py::scope().attr("kXMP_PropValueIsStruct") = static_cast<XMP_Uns32>(kXMP_PropValueIsStruct);
    py::scope().attr("kXMP_PropValueIsArray") = static_cast<XMP_Uns32>(kXMP_PropValueIsArray);
    py::scope().attr("kXMP_PropArrayIsUnordered") = static_cast<XMP_Uns32>(kXMP_PropArrayIsUnordered);
    py::scope().attr("kXMP_PropArrayIsOrdered") = static_cast<XMP_Uns32>(kXMP_PropArrayIsOrdered);
    py::scope().attr("kXMP_PropArrayIsAlternate") = static_cast<XMP_Uns32>(kXMP_PropArrayIsAlternate);
    py::scope().attr("kXMP_PropArrayIsAltText") = static_cast<XMP_Uns32>(kXMP_PropArrayIsAltText);
    py::scope().attr("kXMP_PropIsAlias") = static_cast<XMP_Uns32>(kXMP_PropIsAlias);
    py::scope().attr("kXMP_PropHasAliases") = static_cast<XMP_Uns32>(kXMP_PropHasAliases);
    py::scope().attr("kXMP_PropIsInternal") = static_cast<XMP_Uns32>(kXMP_PropIsInternal);
    py::scope().attr("kXMP_PropIsStable") = static_cast<XMP_Uns32>(kXMP_PropIsStable);
    py::scope().attr("kXMP_PropIsDerived") = static_cast<XMP_Uns32>(kXMP_PropIsDerived);
    py::scope().attr("kXMP_PropArrayFormMask") = static_cast<XMP_Uns32>(kXMP_PropArrayFormMask);
    py::scope().attr("kXMP_PropCompositeMask") = static_cast<XMP_Uns32>(kXMP_PropCompositeMask);
    py::scope().attr("kXMP_ImplReservedMask") = static_cast<XMP_Uns32>(kXMP_ImplReservedMask);
    py::scope().attr("kXMP_InsertBeforeItem") = static_cast<XMP_Uns32>(kXMP_InsertBeforeItem);
    py::scope().attr("kXMP_InsertAfterItem") = static_cast<XMP_Uns32>(kXMP_InsertAfterItem);
    py::scope().attr("kXMP_DeleteExisting") = static_cast<XMP_Uns32>(kXMP_DeleteExisting);
    py::scope().attr("kXMP_PropValueOptionsMask") = static_cast<XMP_Uns32>(kXMP_PropValueOptionsMask);
    py::scope().attr("kXMP_PropArrayLocationMask") = static_cast<XMP_Uns32>(kXMP_PropArrayLocationMask);
    py::scope().attr("kXMP_RequireXMPMeta") = static_cast<XMP_Uns32>(kXMP_RequireXMPMeta);
    py::scope().attr("kXMP_ParseMoreBuffers") = static_cast<XMP_Uns32>(kXMP_ParseMoreBuffers);
    py::scope().attr("kXMP_StrictAliasing") = static_cast<XMP_Uns32>(kXMP_StrictAliasing);
    py::scope().attr("kXMP_OmitPacketWrapper") = static_cast<XMP_Uns32>(kXMP_OmitPacketWrapper);
    py::scope().attr("kXMP_ReadOnlyPacket") = static_cast<XMP_Uns32>(kXMP_ReadOnlyPacket);
    py::scope().attr("kXMP_UseCompactFormat") = static_cast<XMP_Uns32>(kXMP_UseCompactFormat);
    py::scope().attr("kXMP_IncludeThumbnailPad") = static_cast<XMP_Uns32>(kXMP_IncludeThumbnailPad);
    py::scope().attr("kXMP_ExactPacketLength") = static_cast<XMP_Uns32>(kXMP_ExactPacketLength);
    py::scope().attr("kXMP_OmitAllFormatting") = static_cast<XMP_Uns32>(kXMP_OmitAllFormatting);
    py::scope().attr("kXMP_OmitXMPMetaElement") = static_cast<XMP_Uns32>(kXMP_OmitXMPMetaElement);
    py::scope().attr("_XMP_UTF16_Bit") = static_cast<XMP_Uns32>(_XMP_UTF16_Bit);
    py::scope().attr("_XMP_UTF32_Bit") = static_cast<XMP_Uns32>(_XMP_UTF32_Bit);
    py::scope().attr("kXMP_EncodingMask") = static_cast<XMP_Uns32>(kXMP_EncodingMask);
    py::scope().attr("kXMP_EncodeUTF8") = static_cast<XMP_Uns32>(kXMP_EncodeUTF8);
    py::scope().attr("kXMP_EncodeUTF16Big") = static_cast<XMP_Uns32>(kXMP_EncodeUTF16Big);
    py::scope().attr("kXMP_EncodeUTF16Little") = static_cast<XMP_Uns32>(kXMP_EncodeUTF16Little);
    py::scope().attr("kXMP_EncodeUTF32Big") = static_cast<XMP_Uns32>(kXMP_EncodeUTF32Big);
    py::scope().attr("kXMP_EncodeUTF32Little") = static_cast<XMP_Uns32>(kXMP_EncodeUTF32Little);
    py::scope().attr("kXMP_IterClassMask") = static_cast<XMP_Uns32>(kXMP_IterClassMask);
    py::scope().attr("kXMP_IterProperties") = static_cast<XMP_Uns32>(kXMP_IterProperties);
    py::scope().attr("kXMP_IterAliases") = static_cast<XMP_Uns32>(kXMP_IterAliases);
    py::scope().attr("kXMP_IterNamespaces") = static_cast<XMP_Uns32>(kXMP_IterNamespaces);
    py::scope().attr("kXMP_IterJustChildren") = static_cast<XMP_Uns32>(kXMP_IterJustChildren);
    py::scope().attr("kXMP_IterJustLeafNodes") = static_cast<XMP_Uns32>(kXMP_IterJustLeafNodes);
    py::scope().attr("kXMP_IterJustLeafName") = static_cast<XMP_Uns32>(kXMP_IterJustLeafName);
    py::scope().attr("kXMP_IterOmitQualifiers") = static_cast<XMP_Uns32>(kXMP_IterOmitQualifiers);
    py::scope().attr("kXMP_IterSkipSubtree") = static_cast<XMP_Uns32>(kXMP_IterSkipSubtree);
    py::scope().attr("kXMP_IterSkipSiblings") = static_cast<XMP_Uns32>(kXMP_IterSkipSiblings);
    py::scope().attr("kXMPUtil_AllowCommas") = static_cast<XMP_Uns32>(kXMPUtil_AllowCommas);
    py::scope().attr("kXMPTemplate_IncludeInternalProperties") = static_cast<XMP_Uns32>(kXMPTemplate_IncludeInternalProperties);
    py::scope().attr("kXMPTemplate_ReplaceExistingProperties") = static_cast<XMP_Uns32>(kXMPTemplate_ReplaceExistingProperties);
    py::scope().attr("kXMPTemplate_ReplaceWithDeleteEmpty") = static_cast<XMP_Uns32>(kXMPTemplate_ReplaceWithDeleteEmpty);
    py::scope().attr("kXMPTemplate_AddNewProperties") = static_cast<XMP_Uns32>(kXMPTemplate_AddNewProperties);
    py::scope().attr("kXMPTemplate_ClearUnnamedProperties") = static_cast<XMP_Uns32>(kXMPTemplate_ClearUnnamedProperties);
    py::scope().attr("kXMPUtil_DoAllProperties") = static_cast<XMP_Uns32>(kXMPUtil_DoAllProperties);
    py::scope().attr("kXMPUtil_ReplaceOldValues") = static_cast<XMP_Uns32>(kXMPUtil_ReplaceOldValues);
    py::scope().attr("kXMPUtil_DeleteEmptyValues") = static_cast<XMP_Uns32>(kXMPUtil_DeleteEmptyValues);
    py::scope().attr("kXMPUtil_IncludeAliases") = static_cast<XMP_Uns32>(kXMPUtil_IncludeAliases);
    py::scope().attr("kXMP_PDFFile") = static_cast<XMP_Uns32>(kXMP_PDFFile);
    py::scope().attr("kXMP_PostScriptFile") = static_cast<XMP_Uns32>(kXMP_PostScriptFile);
    py::scope().attr("kXMP_EPSFile") = static_cast<XMP_Uns32>(kXMP_EPSFile);
    py::scope().attr("kXMP_JPEGFile") = static_cast<XMP_Uns32>(kXMP_JPEGFile);
    py::scope().attr("kXMP_JPEG2KFile") = static_cast<XMP_Uns32>(kXMP_JPEG2KFile);
    py::scope().attr("kXMP_TIFFFile") = static_cast<XMP_Uns32>(kXMP_TIFFFile);
    py::scope().attr("kXMP_GIFFile") = static_cast<XMP_Uns32>(kXMP_GIFFile);
    py::scope().attr("kXMP_PNGFile") = static_cast<XMP_Uns32>(kXMP_PNGFile);
    py::scope().attr("kXMP_SWFFile") = static_cast<XMP_Uns32>(kXMP_SWFFile);
    py::scope().attr("kXMP_FLAFile") = static_cast<XMP_Uns32>(kXMP_FLAFile);
    py::scope().attr("kXMP_FLVFile") = static_cast<XMP_Uns32>(kXMP_FLVFile);
    py::scope().attr("kXMP_MOVFile") = static_cast<XMP_Uns32>(kXMP_MOVFile);
    py::scope().attr("kXMP_AVIFile") = static_cast<XMP_Uns32>(kXMP_AVIFile);
    py::scope().attr("kXMP_CINFile") = static_cast<XMP_Uns32>(kXMP_CINFile);
    py::scope().attr("kXMP_WAVFile") = static_cast<XMP_Uns32>(kXMP_WAVFile);
    py::scope().attr("kXMP_MP3File") = static_cast<XMP_Uns32>(kXMP_MP3File);
    py::scope().attr("kXMP_SESFile") = static_cast<XMP_Uns32>(kXMP_SESFile);
    py::scope().attr("kXMP_CELFile") = static_cast<XMP_Uns32>(kXMP_CELFile);
    py::scope().attr("kXMP_MPEGFile") = static_cast<XMP_Uns32>(kXMP_MPEGFile);
    py::scope().attr("kXMP_MPEG2File") = static_cast<XMP_Uns32>(kXMP_MPEG2File);
    py::scope().attr("kXMP_MPEG4File") = static_cast<XMP_Uns32>(kXMP_MPEG4File);
    py::scope().attr("kXMP_WMAVFile") = static_cast<XMP_Uns32>(kXMP_WMAVFile);
    py::scope().attr("kXMP_AIFFFile") = static_cast<XMP_Uns32>(kXMP_AIFFFile);
    py::scope().attr("kXMP_P2File") = static_cast<XMP_Uns32>(kXMP_P2File);
    py::scope().attr("kXMP_XDCAM_FAMFile") = static_cast<XMP_Uns32>(kXMP_XDCAM_FAMFile);
    py::scope().attr("kXMP_XDCAM_SAMFile") = static_cast<XMP_Uns32>(kXMP_XDCAM_SAMFile);
    py::scope().attr("kXMP_XDCAM_EXFile") = static_cast<XMP_Uns32>(kXMP_XDCAM_EXFile);
    py::scope().attr("kXMP_AVCHDFile") = static_cast<XMP_Uns32>(kXMP_AVCHDFile);
    py::scope().attr("kXMP_SonyHDVFile") = static_cast<XMP_Uns32>(kXMP_SonyHDVFile);
    py::scope().attr("kXMP_HTMLFile") = static_cast<XMP_Uns32>(kXMP_HTMLFile);
    py::scope().attr("kXMP_XMLFile") = static_cast<XMP_Uns32>(kXMP_XMLFile);
    py::scope().attr("kXMP_TextFile") = static_cast<XMP_Uns32>(kXMP_TextFile);
    py::scope().attr("kXMP_PhotoshopFile") = static_cast<XMP_Uns32>(kXMP_PhotoshopFile);
    py::scope().attr("kXMP_IllustratorFile") = static_cast<XMP_Uns32>(kXMP_IllustratorFile);
    py::scope().attr("kXMP_InDesignFile") = static_cast<XMP_Uns32>(kXMP_InDesignFile);
    py::scope().attr("kXMP_AEProjectFile") = static_cast<XMP_Uns32>(kXMP_AEProjectFile);
    py::scope().attr("kXMP_AEProjTemplateFile") = static_cast<XMP_Uns32>(kXMP_AEProjTemplateFile);
    py::scope().attr("kXMP_AEFilterPresetFile") = static_cast<XMP_Uns32>(kXMP_AEFilterPresetFile);
    py::scope().attr("kXMP_EncoreProjectFile") = static_cast<XMP_Uns32>(kXMP_EncoreProjectFile);
    py::scope().attr("kXMP_PremiereProjectFile") = static_cast<XMP_Uns32>(kXMP_PremiereProjectFile);
    py::scope().attr("kXMP_PremiereTitleFile") = static_cast<XMP_Uns32>(kXMP_PremiereTitleFile);
    py::scope().attr("kXMP_UCFFile") = static_cast<XMP_Uns32>(kXMP_UCFFile);
    py::scope().attr("kXMP_UnknownFile") = static_cast<XMP_Uns32>(kXMP_UnknownFile);
    py::scope().attr("kXMP_CharLittleEndianMask") = static_cast<XMP_Uns32>(kXMP_CharLittleEndianMask);
    py::scope().attr("kXMP_Char16BitMask") = static_cast<XMP_Uns32>(kXMP_Char16BitMask);
    py::scope().attr("kXMP_Char32BitMask") = static_cast<XMP_Uns32>(kXMP_Char32BitMask);
    py::scope().attr("kXMP_Char8Bit") = static_cast<XMP_Uns32>(kXMP_Char8Bit);
    py::scope().attr("kXMP_Char16BitBig") = static_cast<XMP_Uns32>(kXMP_Char16BitBig);
    py::scope().attr("kXMP_Char16BitLittle") = static_cast<XMP_Uns32>(kXMP_Char16BitLittle);
    py::scope().attr("kXMP_Char32BitBig") = static_cast<XMP_Uns32>(kXMP_Char32BitBig);
    py::scope().attr("kXMP_Char32BitLittle") = static_cast<XMP_Uns32>(kXMP_Char32BitLittle);
    py::scope().attr("kXMP_CharUnknown") = static_cast<XMP_Uns32>(kXMP_CharUnknown);
    py::scope().attr("kXMP_PacketInfoVersion") = static_cast<XMP_Uns32>(kXMP_PacketInfoVersion);
    py::scope().attr("kXMPFiles_IgnoreLocalText") = static_cast<XMP_Uns32>(kXMPFiles_IgnoreLocalText);
    py::scope().attr("kXMPFiles_ServerMode") = static_cast<XMP_Uns32>(kXMPFiles_ServerMode);
    py::scope().attr("kXMPFiles_CanInjectXMP") = static_cast<XMP_Uns32>(kXMPFiles_CanInjectXMP);
    py::scope().attr("kXMPFiles_CanExpand") = static_cast<XMP_Uns32>(kXMPFiles_CanExpand);
    py::scope().attr("kXMPFiles_CanRewrite") = static_cast<XMP_Uns32>(kXMPFiles_CanRewrite);
    py::scope().attr("kXMPFiles_PrefersInPlace") = static_cast<XMP_Uns32>(kXMPFiles_PrefersInPlace);
    py::scope().attr("kXMPFiles_CanReconcile") = static_cast<XMP_Uns32>(kXMPFiles_CanReconcile);
    py::scope().attr("kXMPFiles_AllowsOnlyXMP") = static_cast<XMP_Uns32>(kXMPFiles_AllowsOnlyXMP);
    py::scope().attr("kXMPFiles_ReturnsRawPacket") = static_cast<XMP_Uns32>(kXMPFiles_ReturnsRawPacket);
    py::scope().attr("kXMPFiles_HandlerOwnsFile") = static_cast<XMP_Uns32>(kXMPFiles_HandlerOwnsFile);
    py::scope().attr("kXMPFiles_AllowsSafeUpdate") = static_cast<XMP_Uns32>(kXMPFiles_AllowsSafeUpdate);
    py::scope().attr("kXMPFiles_NeedsReadOnlyPacket") = static_cast<XMP_Uns32>(kXMPFiles_NeedsReadOnlyPacket);
    py::scope().attr("kXMPFiles_UsesSidecarXMP") = static_cast<XMP_Uns32>(kXMPFiles_UsesSidecarXMP);
    py::scope().attr("kXMPFiles_FolderBasedFormat") = static_cast<XMP_Uns32>(kXMPFiles_FolderBasedFormat);
    py::scope().attr("kXMPFiles_OpenForRead") = static_cast<XMP_Uns32>(kXMPFiles_OpenForRead);
    py::scope().attr("kXMPFiles_OpenForUpdate") = static_cast<XMP_Uns32>(kXMPFiles_OpenForUpdate);
    py::scope().attr("kXMPFiles_OpenOnlyXMP") = static_cast<XMP_Uns32>(kXMPFiles_OpenOnlyXMP);
    py::scope().attr("kXMPFiles_OpenStrictly") = static_cast<XMP_Uns32>(kXMPFiles_OpenStrictly);
    py::scope().attr("kXMPFiles_OpenUseSmartHandler") = static_cast<XMP_Uns32>(kXMPFiles_OpenUseSmartHandler);
    py::scope().attr("kXMPFiles_OpenUsePacketScanning") = static_cast<XMP_Uns32>(kXMPFiles_OpenUsePacketScanning);
    py::scope().attr("kXMPFiles_OpenLimitedScanning") = static_cast<XMP_Uns32>(kXMPFiles_OpenLimitedScanning);
    py::scope().attr("kXMPFiles_OpenRepairFile") = static_cast<XMP_Uns32>(kXMPFiles_OpenRepairFile);
    py::scope().attr("kXMPFiles_UpdateSafely") = static_cast<XMP_Uns32>(kXMPFiles_UpdateSafely);
    py::scope().attr("kXMPErr_Unknown") = static_cast<XMP_Uns32>(kXMPErr_Unknown);
    py::scope().attr("kXMPErr_TBD") = static_cast<XMP_Uns32>(kXMPErr_TBD);
    py::scope().attr("kXMPErr_Unavailable") = static_cast<XMP_Uns32>(kXMPErr_Unavailable);
    py::scope().attr("kXMPErr_BadObject") = static_cast<XMP_Uns32>(kXMPErr_BadObject);
    py::scope().attr("kXMPErr_BadParam") = static_cast<XMP_Uns32>(kXMPErr_BadParam);
    py::scope().attr("kXMPErr_BadValue") = static_cast<XMP_Uns32>(kXMPErr_BadValue);
    py::scope().attr("kXMPErr_AssertFailure") = static_cast<XMP_Uns32>(kXMPErr_AssertFailure);
    py::scope().attr("kXMPErr_EnforceFailure") = static_cast<XMP_Uns32>(kXMPErr_EnforceFailure);
    py::scope().attr("kXMPErr_Unimplemented") = static_cast<XMP_Uns32>(kXMPErr_Unimplemented);
    py::scope().attr("kXMPErr_InternalFailure") = static_cast<XMP_Uns32>(kXMPErr_InternalFailure);
    py::scope().attr("kXMPErr_Deprecated") = static_cast<XMP_Uns32>(kXMPErr_Deprecated);
    py::scope().attr("kXMPErr_ExternalFailure") = static_cast<XMP_Uns32>(kXMPErr_ExternalFailure);
    py::scope().attr("kXMPErr_UserAbort") = static_cast<XMP_Uns32>(kXMPErr_UserAbort);
    py::scope().attr("kXMPErr_StdException") = static_cast<XMP_Uns32>(kXMPErr_StdException);
    py::scope().attr("kXMPErr_UnknownException") = static_cast<XMP_Uns32>(kXMPErr_UnknownException);
    py::scope().attr("kXMPErr_NoMemory") = static_cast<XMP_Uns32>(kXMPErr_NoMemory);
    py::scope().attr("kXMPErr_BadSchema") = static_cast<XMP_Uns32>(kXMPErr_BadSchema);
    py::scope().attr("kXMPErr_BadXPath") = static_cast<XMP_Uns32>(kXMPErr_BadXPath);
    py::scope().attr("kXMPErr_BadOptions") = static_cast<XMP_Uns32>(kXMPErr_BadOptions);
    py::scope().attr("kXMPErr_BadIndex") = static_cast<XMP_Uns32>(kXMPErr_BadIndex);
    py::scope().attr("kXMPErr_BadIterPosition") = static_cast<XMP_Uns32>(kXMPErr_BadIterPosition);
    py::scope().attr("kXMPErr_BadParse") = static_cast<XMP_Uns32>(kXMPErr_BadParse);
    py::scope().attr("kXMPErr_BadSerialize") = static_cast<XMP_Uns32>(kXMPErr_BadSerialize);
    py::scope().attr("kXMPErr_BadFileFormat") = static_cast<XMP_Uns32>(kXMPErr_BadFileFormat);
    py::scope().attr("kXMPErr_NoFileHandler") = static_cast<XMP_Uns32>(kXMPErr_NoFileHandler);
    py::scope().attr("kXMPErr_TooLargeForJPEG") = static_cast<XMP_Uns32>(kXMPErr_TooLargeForJPEG);
    py::scope().attr("kXMPErr_BadXML") = static_cast<XMP_Uns32>(kXMPErr_BadXML);
    py::scope().attr("kXMPErr_BadRDF") = static_cast<XMP_Uns32>(kXMPErr_BadRDF);
    py::scope().attr("kXMPErr_BadXMP") = static_cast<XMP_Uns32>(kXMPErr_BadXMP);
    py::scope().attr("kXMPErr_EmptyIterator") = static_cast<XMP_Uns32>(kXMPErr_EmptyIterator);
    py::scope().attr("kXMPErr_BadUnicode") = static_cast<XMP_Uns32>(kXMPErr_BadUnicode);
    py::scope().attr("kXMPErr_BadTIFF") = static_cast<XMP_Uns32>(kXMPErr_BadTIFF);
    py::scope().attr("kXMPErr_BadJPEG") = static_cast<XMP_Uns32>(kXMPErr_BadJPEG);
    py::scope().attr("kXMPErr_BadPSD") = static_cast<XMP_Uns32>(kXMPErr_BadPSD);
    py::scope().attr("kXMPErr_BadPSIR") = static_cast<XMP_Uns32>(kXMPErr_BadPSIR);
    py::scope().attr("kXMPErr_BadIPTC") = static_cast<XMP_Uns32>(kXMPErr_BadIPTC);
    py::scope().attr("kXMPErr_BadMPEG") = static_cast<XMP_Uns32>(kXMPErr_BadMPEG);
    
    py::class_<XMP_VersionInfo>("XMP_VersionInfo")
    .def_readwrite("major", &XMP_VersionInfo::major)
    .def_readwrite("minor", &XMP_VersionInfo::minor)
    .def_readwrite("micro", &XMP_VersionInfo::micro)
    .def_readwrite("isDebug", &XMP_VersionInfo::isDebug)
    .def_readwrite("build", &XMP_VersionInfo::build)
    .def_readwrite("flags", &XMP_VersionInfo::flags)
    .def_readwrite("message", &XMP_VersionInfo::message)
    ;

    py::class_<XMP_PacketInfo>("XMP_PacketInfo")
    .def_readwrite("offset", &XMP_PacketInfo::offset)
    .def_readwrite("length", &XMP_PacketInfo::length)
    .def_readwrite("padSize", &XMP_PacketInfo::padSize)
    .def_readwrite("charForm", &XMP_PacketInfo::charForm)
    .def_readwrite("writeable", &XMP_PacketInfo::writeable)
    .def_readwrite("hasWrapper", &XMP_PacketInfo::hasWrapper)
    .def_readwrite("pad", &XMP_PacketInfo::pad)
    ;

    void    (*Initialize1)()                                                                    = &xmp::SXMPFiles::Initialize;
    void    (*Initialize2)(XMP_OptionBits)                                                      = &xmp::SXMPFiles::Initialize;
    void    (xmp::SXMPFiles::*PutXMP1)(const xmp::SXMPMeta& xmpObj)                             = &xmp::SXMPFiles::PutXMP;
    void    (xmp::SXMPFiles::*PutXMP2)(const TXMP_STRING_TYPE& xmpPacket)                       = &xmp::SXMPFiles::PutXMP;
    bool    (xmp::SXMPFiles::*CanPutXMP1)(const xmp::SXMPMeta& xmpObj)                          = &xmp::SXMPFiles::CanPutXMP;
    bool    (xmp::SXMPFiles::*CanPutXMP2)(const TXMP_STRING_TYPE& xmpPacket)                    = &xmp::SXMPFiles::CanPutXMP;
    
    py::class_<xmp::SXMPFiles>("SXMPFiles", py::init<>())
    .def(py::init<XMP_StringPtr, XMP_FileFormat, XMP_OptionBits>())
    .def("GetVersionInfo", &xmp::SXMPFiles::GetVersionInfo)
    .staticmethod("GetVersionInfo")
    .def("Initialize", Initialize1)
    .def("Initialize", Initialize2)
    .staticmethod("Initialize")
    .def("Terminate", &xmp::SXMPFiles::Terminate)
    .staticmethod("Terminate")
    .def("GetFormatInfo", &xmp::SXMPFiles::GetFormatInfo)    
    .staticmethod("GetFormatInfo")
    .def("CheckFileFormat", &xmp::SXMPFiles::CheckFileFormat)
    .staticmethod("CheckFileFormat")
    .def("CheckPackageFormat", &xmp::SXMPFiles::CheckPackageFormat)
    .staticmethod("CheckPackageFormat")
    .def("OpenFile", &xmp::SXMPFiles::OpenFile, OpenFile_overloads())
    .def("CloseFile", &xmp::SXMPFiles::CloseFile, CloseFile_overloads())
    .def("GetFileInfo", &xmp::SXMPFiles::GetFileInfo)
    .def("SetAbortProc", &xmp::SXMPFiles::SetAbortProc)
    .def("GetXMP", &xmp::SXMPFiles::GetXMP)
    .def("PutXMP", PutXMP1)
    .def("PutXMP", PutXMP2)
    .def("CanPutXMP", CanPutXMP1)
    .def("CanPutXMP", CanPutXMP2)
    ;
    
    TXMP_STRING_TYPE (xmp::SXMPMeta::*SerializeToBuffer1)(XMP_OptionBits, XMP_StringLen, XMP_StringPtr, XMP_StringPtr, XMP_Index) const  = &xmp::SXMPMeta::SerializeToBuffer;
    TXMP_STRING_TYPE (xmp::SXMPMeta::*SerializeToBuffer2)(XMP_OptionBits, XMP_StringLen) const                                           = &xmp::SXMPMeta::SerializeToBuffer;

    py::class_<xmp::SXMPMeta>("SXMPMeta", py::init<>())
    .def(py::init<const xmp::SXMPMeta&>())    
    .def(py::init<const TXMP_STRING_TYPE&>())
    .def("GetVersionInfo", &xmp::SXMPMeta::GetVersionInfo)
    .staticmethod("GetVersionInfo")
    .def("Initialize", &xmp::SXMPMeta::Initialize)
    .staticmethod("Initialize")
    .def("Terminate", &xmp::SXMPMeta::Terminate)
    .staticmethod("Terminate")
    .def("GetGlobalOptions", &xmp::SXMPMeta::GetGlobalOptions)
    .staticmethod("GetGlobalOptions")
    .def("SetGlobalOptions", &xmp::SXMPMeta::SetGlobalOptions)
    .staticmethod("SetGlobalOptions")
    .def("DumpNamespaces", &xmp::SXMPMeta::DumpNamespaces)
    .staticmethod("DumpNamespaces")
    .def("RegisterNamespace", &xmp::SXMPMeta::RegisterNamespace)
    .staticmethod("RegisterNamespace")
    .def("GetNamespacePrefix", &xmp::SXMPMeta::GetNamespacePrefix)
    .staticmethod("GetNamespacePrefix")
    .def("GetNamespaceURI", &xmp::SXMPMeta::GetNamespaceURI)
    .staticmethod("GetNamespaceURI")
    .def("DeleteNamespace", &xmp::SXMPMeta::DeleteNamespace)
    .staticmethod("DeleteNamespace")
    .def("GetProperty", &xmp::SXMPMeta::GetProperty)
    .def("GetArrayItem", &xmp::SXMPMeta::GetArrayItem)
    .def("GetStructField", &xmp::SXMPMeta::GetStructField)
    .def("GetQualifier", &xmp::SXMPMeta::GetQualifier)
    .def("SetProperty", &xmp::SXMPMeta::SetProperty, SetProperty_overloads())
    .def("SetArrayItem", &xmp::SXMPMeta::SetArrayItem, SetArrayItem_overloads())
    .def("AppendArrayItem", &xmp::SXMPMeta::AppendArrayItem, AppendArrayItem_overloads())
    .def("SetStructField", &xmp::SXMPMeta::SetStructField, SetStructField_overloads())
    .def("SetQualifier", &xmp::SXMPMeta::SetQualifier, SetQualifier_overloads())
    .def("DeleteProperty", &xmp::SXMPMeta::DeleteProperty)
    .def("DeleteArrayItem", &xmp::SXMPMeta::DeleteArrayItem)
    .def("DeleteStructField", &xmp::SXMPMeta::DeleteStructField)
    .def("DeleteQualifier", &xmp::SXMPMeta::DeleteQualifier)
    .def("DoesPropertyExist", &xmp::SXMPMeta::DoesPropertyExist)
    .def("DoesArrayItemExist", &xmp::SXMPMeta::DoesArrayItemExist)
    .def("DoesStructFieldExist", &xmp::SXMPMeta::DoesStructFieldExist)
    .def("DoesQualifierExist", &xmp::SXMPMeta::DoesQualifierExist)
    .def("GetProperty_Bool", &xmp::SXMPMeta::GetProperty_Bool)
    .def("GetProperty_Int", &xmp::SXMPMeta::GetProperty_Int)
    .def("GetProperty_Int64", &xmp::SXMPMeta::GetProperty_Int64)
    .def("GetProperty_Float", &xmp::SXMPMeta::GetProperty_Float)
    .def("GetProperty_Date", &xmp::SXMPMeta::GetProperty_Date)
    .def("SetProperty_Bool", &xmp::SXMPMeta::SetProperty_Bool, SetProperty_Bool_overloads())
    .def("SetProperty_Int", &xmp::SXMPMeta::SetProperty_Int, SetProperty_Int_overloads())
    .def("SetProperty_Int64", &xmp::SXMPMeta::SetProperty_Int64, SetProperty_Int64_overloads())
    .def("SetProperty_Float", &xmp::SXMPMeta::SetProperty_Float, SetProperty_Float_overloads())
    .def("SetProperty_Date", &xmp::SXMPMeta::SetProperty_Date, SetProperty_Date_overloads())
    .def("GetLocalizedText", &xmp::SXMPMeta::GetLocalizedText)
    .def("SetLocalizedText", &xmp::SXMPMeta::SetLocalizedText, SetLocalizedText_overloads())
    .def("ParseFromBuffer", &xmp::SXMPMeta::ParseFromBuffer, ParseFromBuffer_overloads())
    .def("SerializeToBuffer", SerializeToBuffer1, SerializeToBuffer1_overloads())
    .def("SerializeToBuffer", SerializeToBuffer2, SerializeToBuffer2_overloads())
//    .def("GetInternalRef", &xmp::SXMPMeta::GetInternalRef)
    .def("GetObjectName", &xmp::SXMPMeta::GetObjectName)
    .def("SetObjectName", &xmp::SXMPMeta::SetObjectName)
    .def("Sort", &xmp::SXMPMeta::Sort)
    .def("Erase", &xmp::SXMPMeta::Erase)
    .def("Clone", &xmp::SXMPMeta::Clone, Clone_overloads())
    .def("CountArrayItems", &xmp::SXMPMeta::CountArrayItems)
    .def("DumpObject", &xmp::SXMPMeta::DumpObject)
    .def("GetObjectOptions", &xmp::SXMPMeta::GetObjectOptions)
    .def("SetObjectOptions", &xmp::SXMPMeta::SetObjectOptions)
    ;

    py::class_<xmp::SXMPIterator>("SXMPIterator", py::init<const xmp::SXMPMeta&, XMP_StringPtr, XMP_StringPtr, py::optional<XMP_OptionBits> >())
    .def(py::init<const xmp::SXMPMeta&, XMP_StringPtr, py::optional<XMP_OptionBits> >())
    .def(py::init<const xmp::SXMPMeta&, py::optional<XMP_OptionBits> >())
    .def(py::init<XMP_StringPtr, XMP_StringPtr, XMP_OptionBits>())
    .def("Next", &xmp::SXMPIterator::Next)
    .def("Skip", &xmp::SXMPIterator::Skip)
    ;

    py::class_<xmp::SXMPUtils>("SXMPUtils")
    .def("ComposeArrayItemPath", xmp::SXMPUtils::ComposeArrayItemPath)
    .staticmethod("ComposeArrayItemPath")
    .def("ComposeStructFieldPath", xmp::SXMPUtils::ComposeStructFieldPath)
    .staticmethod("ComposeStructFieldPath")
    .def("ComposeQualifierPath", xmp::SXMPUtils::ComposeQualifierPath)
    .staticmethod("ComposeQualifierPath")
    .def("ComposeLangSelector", xmp::SXMPUtils::ComposeLangSelector)
    .staticmethod("ComposeLangSelector")
    .def("ComposeFieldSelector", xmp::SXMPUtils::ComposeFieldSelector)
    .staticmethod("ComposeFieldSelector")
    .def("ConvertFromBool", xmp::SXMPUtils::ConvertFromBool)
    .staticmethod("ConvertFromBool")
    .def("ConvertFromInt", xmp::SXMPUtils::ConvertFromInt)
    .staticmethod("ConvertFromInt")
    .def("ConvertFromInt64", xmp::SXMPUtils::ConvertFromInt64)
    .staticmethod("ConvertFromInt64")
    .def("ConvertFromFloat", xmp::SXMPUtils::ConvertFromFloat)
    .staticmethod("ConvertFromFloat")
    .def("ConvertFromDate", xmp::SXMPUtils::ConvertFromDate)
    .staticmethod("ConvertFromDate")
    .def("ConvertToBool", xmp::SXMPUtils::ConvertToBool)
    .staticmethod("ConvertToBool")
    .def("ConvertToInt", xmp::SXMPUtils::ConvertToInt)
    .staticmethod("ConvertToInt")
    .def("ConvertToInt64", xmp::SXMPUtils::ConvertToInt64)
    .staticmethod("ConvertToInt64")
    .def("ConvertToFloat", xmp::SXMPUtils::ConvertToFloat)
    .staticmethod("ConvertToFloat")
    .def("ConvertToDate", xmp::SXMPUtils::ConvertToDate)
    .staticmethod("ConvertToDate")
    .def("CurrentDateTime", xmp::SXMPUtils::CurrentDateTime)
    .staticmethod("CurrentDateTime")
    .def("SetTimeZone", xmp::SXMPUtils::SetTimeZone)
    .staticmethod("SetTimeZone")
    .def("ConvertToUTCTime", xmp::SXMPUtils::ConvertToUTCTime)
    .staticmethod("ConvertToUTCTime")
    .def("ConvertToLocalTime", xmp::SXMPUtils::ConvertToLocalTime)
    .staticmethod("ConvertToLocalTime")
    .def("CompareDateTime", xmp::SXMPUtils::CompareDateTime)
    .staticmethod("CompareDateTime")
    .def("EncodeToBase64", xmp::SXMPUtils::EncodeToBase64)
    .staticmethod("EncodeToBase64")
    .def("DecodeFromBase64", xmp::SXMPUtils::DecodeFromBase64)
    .staticmethod("DecodeFromBase64")
    .def("PackageForJPEG", xmp::SXMPUtils::PackageForJPEG)
    .staticmethod("PackageForJPEG")
    .def("MergeFromJPEG", xmp::SXMPUtils::MergeFromJPEG)
    .staticmethod("MergeFromJPEG")
    .def("CatenateArrayItems", xmp::SXMPUtils::CatenateArrayItems)
    .staticmethod("CatenateArrayItems")
    .def("SeparateArrayItems", xmp::SXMPUtils::SeparateArrayItems)
    .staticmethod("SeparateArrayItems")
    .def("ApplyTemplate", xmp::SXMPUtils::ApplyTemplate)
    .staticmethod("ApplyTemplate")
    .def("RemoveProperties", xmp::SXMPUtils::RemoveProperties, RemoveProperties_overloads())
    .staticmethod("RemoveProperties")
    .def("DuplicateSubtree", xmp::SXMPUtils::DuplicateSubtree, DuplicateSubtree_overloads())
    .staticmethod("DuplicateSubtree")
    ;
}
